package net.wangds.persistence.columnoriented.conv;

import net.wangds.common.base.module.Holder;
import net.wangds.data.Removable;
import net.wangds.data.WithId;
import net.wangds.log.helper.LogHelper;
import net.wangds.persistence.columnoriented.entity.ColumnOrientedEntity;
import net.wangds.persistence.def.entity.DictFieldEntity;
import net.wangds.persistence.def.entity.DictTableEntity;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * .
 * <p></p>
 *
 * @author 王东石 2020/10/30 18:14.
 */
@Component
public class DefaultToColumnsConv<B extends WithId<String> & Removable, T extends ColumnOrientedEntity>
        extends AbstractColumnOrientedConv
        implements ToColumnsConv<B, T> {


    @Override
    public List<T> convert(B source) {
        String beanClassName = source.getClass().getName();

        Holder<List<T>> hld = Holder.of(new ArrayList<>());
        repoDictTbl.findByBeanClass(beanClassName).ifPresent(dctTbl -> loadFieldsAndSetNewVal(source, dctTbl, hld));

        return hld.target;
    }


    /**
     * 从数据库加载对象所有字段，然后从参数<code>source</code>设置对应字段的值，并返回新值得列字段列表.
     *
     * @param source 源对象.
     * @param dctTbl 表定义.
     * @param outHld 输出句柄.
     */
    @SuppressWarnings("unchecked")
    private void loadFieldsAndSetNewVal(B source, DictTableEntity dctTbl, Holder<List<T>> outHld) {
        String schema = dctTbl.getSchemaname();
        String tn = dctTbl.getTablename();
        String clzName = dctTbl.getBeanClass();
        Class<T> clz = getBeanClassByName(clzName);

        List<T> out = outHld.target;

        //获取所有字段定义
        List<DictFieldEntity> fldsDef = repoDictFld.findBySchemanameAndTablename(schema, tn, Sort.by(Sort.Order.asc("seq")));

        //获取数据库中现有值
        List<T> fields = cmpJdbc.executeQuery("select * from " + buildTableName(schema, tn) + " where row_key=?", new String[]{source.getId()}, clz);

        //轮询处理定义的每一个字段
        fldsDef.forEach(fd -> {
            String fieldName = fd.getFieldname();
            //找到字段列对象
            T field = fields.stream().filter(c -> StringUtils.equals(fieldName, c.getField())).findAny().orElseGet(() -> {
                //如果没有，则生成新的字段列对象
                T t = null;
                try {
                    t = (T) getBeanClassByName(dctTbl.getFieldClass()).newInstance();
                    t.setRowKey(source.getId());
                    t.setCreateTm(System.currentTimeMillis());
                    t.setField(fieldName);
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
                return t;
            });
            //更新列对象的值
            if (field != null) {
                updateField(source, field);

                out.add(field);
            }

        });


    }

    private void updateField(B source, T field) {

        PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(field.getClass(), field.getField());
        try {
            Objects.requireNonNull(pd);
            Object nv = pd.getReadMethod().invoke(source);

            if (nv == null) {
                field.setTextVal("");
                field.setMidTextVal("");
                field.setBigTextVal("");
                field.setIntVal(0);
                field.setDecVal(0.0f);
            } else {
                switch (field.getDataType()) {
                    case Text:
                        field.setTextVal((String) nv);
                        break;
                    case MidText:
                        field.setMidTextVal((String) nv);
                        break;
                    case BigText:
                        field.setBigTextVal((String) nv);
                        break;
                    case Int:
                        field.setIntVal(((Number) nv).intValue());
                        break;
                    case Long:
                        field.setLongVal(((Number) nv).longValue());
                        break;
                    case Dec:
                        field.setDecVal(((Number) nv).floatValue());
                        break;
                }
            }

        } catch (IllegalAccessException | InvocationTargetException e) {
            LogHelper.error(e);
        }

    }


    private static String buildTableName(String schema, String tablename) {
        return StringUtils.isEmpty(schema)
                ? tablename
                : (schema + "." + tablename);
    }
}
